package wekaGrexBridge;

import grex.BNFException;
import grex.Environment;
import grex.GP;
import grex.genes.Equals;
import grex.genes.Gene;
import grex.genes.Greater;
import grex.genes.If;
import grex.genes.Less;
import grex.genes.ProbTarget;
import grex.genes.ProbVal;
import grex.genes.ProbVar;
import grex.genes.TargetPred;
import grex.genes.Val;
import grex.genes.Var;
import java.io.Serializable;

import java.util.ArrayList;

import weka.classifiers.Classifier;
import weka.core.Instances;

public abstract class ModelInjection implements Serializable {

    protected Environment environment;

    public abstract Classifier buildModel(Instances instances, ArrayList<Integer> indices, Object... optionalParameters) throws Exception;
    public abstract Classifier buildModel(Instances instances, Object... optionalParameters) throws Exception;
    public abstract GP parseModel(Classifier classifier, Environment environment);

    public Gene createCondition(String column, String operator, String value, Environment env) throws BNFException {
        Gene geneOperator;
        Gene[] children;
        if (operator.equals("=")) {
            geneOperator = new Equals();
            children = geneOperator.getChildren();
        } else {            
            if (operator.equals("<=")) {
                geneOperator = new Less();
                children = geneOperator.getChildren();
            } else { // op = "<=" the same as value > variable
                geneOperator = new Greater();
                children = geneOperator.getChildren();
            }
        }
        
        Gene geneValue = createValueGene(value, column,geneOperator, env);
        Gene geneVariable = createVariableGene(column,geneOperator, env);
        children[0] = geneVariable;
        children[1] = geneValue;
        initGene(geneOperator, env);
        return geneOperator;
    }

    private Gene createVariableGene(String columnName, Gene parent, Environment env) throws BNFException {
        Gene geneVariable;
        int colIndex = env.getData().getColumnByName(columnName);
        if (!env.getOptions().getFITNESS_FUNCTION().contains("Prob")) {
            Var var = new Var();
            var.setVarNr(colIndex);
            var.setConVarNr(colIndex);
            var.setCatVarNr(colIndex);
            geneVariable = var;
        } else {
            ProbVar var = new ProbVar();
            var.setVarNr(colIndex);
            geneVariable = var;
        }
        geneVariable.parent=parent;
        initGene(geneVariable, env);
        return geneVariable;
    }

    private Gene createValueGene(String value, String columnName,Gene parent, Environment env) throws BNFException {
        Gene geneValue;
        Val v = new Val();
        ProbVal pv = new ProbVal();
        int colIndex = env.getData().getColumnByName(columnName);
        double geneInternalValue;
        if (env.isNumericColumn(colIndex)) {
            double val = Double.parseDouble(value);
            geneInternalValue = env.getData().calcInternalNumericalValue(colIndex, val, env.getOptions().getTestFOLD());
            if (!env.getOptions().getFITNESS_FUNCTION().contains("Prob")) {
                v.setValValue(geneInternalValue);
                geneValue = v;
            } else {
                pv.setValValue(geneInternalValue);
                geneValue = pv;
            }
        } else {
            geneInternalValue = env.getInternalCategoryValue(colIndex, value);
            if (!env.getOptions().getFITNESS_FUNCTION().contains("Prob")) {
                v.setValValue(geneInternalValue);
                geneValue = v;
            } else {
                pv.setValValue(geneInternalValue);
                geneValue = pv;
            }
        }
        geneValue.parent=parent;
        initGene(geneValue, env);
        return geneValue;
    }

    public Gene createPredictionGene(Environment env) throws BNFException{
        Gene genePrediction;
        if (!env.getOptions().getFITNESS_FUNCTION().contains("Prob"))
            genePrediction = new TargetPred();
        else
            genePrediction = new ProbTarget();
        initGene(genePrediction, env);
        return genePrediction;
    }

    public Gene createIfGene(Gene condition, Gene leftChild, Gene rightChild, Environment env){
        Gene geneIf = new If();
        Gene[] children = geneIf.getChildren();
        children[0] = condition;
        children[1] = leftChild;
        children[2] = rightChild;
        for(Gene child:children)
            child.parent=geneIf;
        initGene(geneIf, env);
        return geneIf;
    }

    private void initGene(Gene gene, Environment env) {
        gene.setEnvironment(env);
        gene.setOptions(env.getOptions());
        gene.setFold(env.getOptions().getTestFOLD());
    }
}
